package tmt.usercenter.web.domain.security;

import com.tmt.annotation.parser.EnumAnnotationParser;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import tmt.usercenter.web.domain.TmtAuthority;
import tmt.usercenter.web.domain.TmtRole;
import tmt.usercenter.web.domain.TmtRoleAuthority;
import tmt.usercenter.web.domain.TmtUser;
import tmt.usercenter.web.domain.enums.AuthorityType;
import tmt.usercenter.web.domain.enums.OperationType;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Getter
@Setter
@EqualsAndHashCode(of = {"userId", "clientId"})
public class UserDetailsImpl implements UserDetails {

    /**
     * 用户编号，唯一识别标识
     */
    private Long userId;

    /**
     * 用户在客户系统中的编号
     */
    private Long userIdInClient;

    /**
     * 用户当前登录的系统ID
     */
    private String clientId;

    /**
     * 用户帐号名，唯一识别
     */
    private String username;

    /**
     * 系统内昵称，可重复
     */
    private String nickname;

    /**
     * 用户真实姓名
     */
    private String realName;

    /**
     * 用户生日
     */
    private Date birthday;

    /**
     * 身份证号
     */
    private String idCard;

    /**
     * 头像
     */
    private String avatar;

    private String password;

    private String email;

    private boolean emailEnabled;

    private String mobile;

    private boolean mobileEnabled;

    private Long externalCode;

    private Long introducerCode;

    /**
     * 会员星级
     */
    private int starLevel = 0;

    /**
     * 用户类型
     */
    private String userType;

    private boolean enabled;

    private boolean accountNonExpired;

    private boolean accountNonLocked;

    private boolean credentialsNonExpired;

    /**
     * 是否已经实名认证了。
     */
    private boolean realNameChecked;

    /**
     * 用户权限记录
     */
    private List<TmtAuthority> tmtAuthorities = new ArrayList<>();

    private List<GrantedAuthority> authorities = new ArrayList<>();

    public UserDetailsImpl() {
    }

    /**
     * 本函数用于第三方客户系统中
     *
     * @param map
     */
    public UserDetailsImpl(Map<String, Object> map) {
        if (map == null)
            return;

        Map<String, Object> principal = map;

        Date birthday = new Date();
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            birthday = sdf.parse((String) principal.get("birthday"));
        } catch (Exception ex) {
        }

        Long externalCode = null;
        try {
            externalCode = Long.parseLong(principal.get("externalCode").toString());
            if (externalCode <= 0)
                externalCode = null;
        } catch (Exception ex) {
        }

        Long introducerCode = null;
        try {
            introducerCode = Long.parseLong(principal.get("introducerCode").toString());
            if (introducerCode <= 0)
                introducerCode = null;
        } catch (Exception ex) {
        }

        this.setUserId(Long.parseLong(principal.get("userId").toString()));
        this.setClientId((String) principal.get("clientId"));
        this.setExternalCode(externalCode);
        this.setIntroducerCode(introducerCode);
        this.setUsername((String) principal.get("username"));
        this.setNickname((String) principal.get("nickname"));
        this.setRealName((String) principal.get("realName"));
        this.setIdCard((String) principal.get("idCard"));
        this.setBirthday(birthday);
        this.setAvatar((String) principal.get("avatar"));
        this.setPassword((String) principal.get("password"));
        this.setUserType((String) principal.get("userType"));
        this.setEnabled((boolean) principal.get("enabled"));
        this.setEmail((String) principal.get("email"));
        this.setMobile((String) principal.get("mobile"));
        this.setStarLevel((int) principal.get("starLevel"));
        this.setEmailEnabled((boolean) principal.get("emailEnabled"));
        this.setMobileEnabled((boolean) principal.get("mobileEnabled"));
        this.setRealNameChecked((boolean) principal.get("realNameChecked"));
        this.setAccountNonLocked((boolean) principal.get("accountNonLocked"));
        this.setAccountNonExpired((boolean) principal.get("accountNonExpired"));
        this.setCredentialsNonExpired((boolean) principal.get("credentialsNonExpired"));

        Vector<GrantedAuthority> grantedAuthorities = new Vector<>();
        this.setAuthorities(grantedAuthorities);

        List<Map<String, String>> list = (List) principal.get("authorities");
        list.forEach(x -> grantedAuthorities.add(new TmtGrantedAuthority(x.get("authority"))));

        Vector<TmtAuthority> tmtAuthorities = new Vector<>();
        this.setTmtAuthorities(tmtAuthorities);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Map<TmtAuthority, String> parentMap = new HashMap<>();
        List<Map<String, Object>> list1 = (List) principal.get("tmtAuthorities");
        list1.forEach(x -> {
            try {
                TmtAuthority authority = new TmtAuthority();
                authority.setId(Long.parseLong(x.get("id").toString()));
                authority.setCreateTime(sdf.parse((String) x.get("createTime")));
                authority.setName((String) x.get("name"));
                authority.setAuthority((String) x.get("authority"));
                authority.setParentCode((String) x.get("parentCode"));
                authority.setDescription((String) x.get("description"));
                authority.setClientTag((String) x.get("clientTag"));
                authority.setClientId((String) x.get("clientId"));
                authority.setAuthorityType(EnumAnnotationParser.convertFromTitle(AuthorityType.class, (String) x.get("authorityType")));
                authority.setOperationType(EnumAnnotationParser.convertFromTitle(OperationType.class, (String) x.get("operationType")));
                authority.setAccessPoint((List) x.get("accessPoint"));
                authority.setIcon((String) x.get("icon"));
                authority.setSort((int) x.get("sort"));
                authority.setVisible((boolean) x.get("visible"));
                authority.setChildren(null);
                authority.setOriginSystem(null);
                authority.setRoleAuthorities(null);
                authority.setCreator(null);
                tmtAuthorities.add(authority);

                if (!StringUtils.isEmpty(authority.getParentCode()))
                    parentMap.put(authority, authority.getParentCode());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });

        parentMap.keySet().forEach(x -> {
            String code = parentMap.get(x);
            Optional<TmtAuthority> optional = tmtAuthorities.stream().filter(y -> Objects.equals(y.getAuthority(), code)).findFirst();
            optional.ifPresent(x::setParent);
        });

    }

    public UserDetailsImpl(TmtUser tmtUser, String clientId, List<TmtRole> tmtRoles) {
        init(tmtUser, clientId, tmtRoles);
    }

    public void setFromUser(TmtUser user, String clientId, List<TmtRole> tmtRoles) {
        init(user, clientId, tmtRoles);
    }

    private void init(TmtUser tmtUser, String clientId, List<TmtRole> tmtRoles) {
        this.userId = tmtUser.getId();
        this.clientId = clientId;
        this.username = tmtUser.getUsername();
        this.nickname = tmtUser.getNickname();
        this.realName = !StringUtils.isEmpty(tmtUser.getRealName()) ? tmtUser.getRealName() : null;
        this.idCard = !StringUtils.isEmpty(tmtUser.getIdCardNum()) ? tmtUser.getIdCardNum() : null;
        this.birthday = tmtUser.getBirthday();
        this.avatar = tmtUser.getAvatar();
        this.password = tmtUser.getPassword();
        this.starLevel = tmtUser.getStarLevel();
        this.externalCode = tmtUser.getExternalCode();
        this.introducerCode = tmtUser.getIntroducerCode();
        this.email = tmtUser.getEmail();
        this.mobile = tmtUser.getMobile();
        this.emailEnabled = tmtUser.isEmailEnabled();
        this.mobileEnabled = tmtUser.isMobileEnabled();
        this.realNameChecked = tmtUser.isRealNameChecked();
        this.userType = EnumAnnotationParser.getTitle(tmtUser.getUserType());
        this.setEnabled(tmtUser.isEnabled());
        this.setAccountNonExpired(true);
        this.setAccountNonLocked(true);
        this.setCredentialsNonExpired(true);

        try {
            //权限仅加载用户本身来源系统的相关权限。
			/*List<TmtRole> roles = tmtUser.getUserRoles().stream()
					.filter(x -> Objects.equals(x.getRole().getOriginSystem().getId(), tmtUser.getOriginSystem().getId()))
					.map(TmtUserRole::getRole)
					.collect(Collectors.toList());*/
            fillAuthorities(this, tmtRoles);
        } catch (Exception ex) {
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * 将用户详情对象的权限相关列表初始化为指定角色组的权限列表
     *
     * @param details
     * @param tmtRoles
     */
    public static void fillAuthorities(UserDetailsImpl details, List<TmtRole> tmtRoles) {
        details.getAuthorities().clear();
        try {
            List<GrantedAuthority> list = (List) details.getAuthorities();
            tmtRoles.stream().forEach(x -> {
                if (list.stream().noneMatch(y -> Objects.equals(x.getCode(), y.getAuthority())))
                    list.add(new TmtGrantedAuthority("ROLE_" + x.getCode()));

                x.getRoleAuthorities().forEach(y -> {
                    if (list.stream().noneMatch(z -> Objects.equals(z.getAuthority(), y.getAuthority().getAuthority())))
                        list.add(new TmtGrantedAuthority(y.getAuthority().getAuthority()));
                });
            });
        } catch (Exception ex) {
        }


        details.getTmtAuthorities().clear();
        try {
            List<TmtAuthority> authorities = new ArrayList<>();
            Map<Long, Long> parentMap = new HashMap<>();

            Optional<List<TmtAuthority>> optional = tmtRoles.stream()
                    .map(x -> x.getRoleAuthorities().stream().map(TmtRoleAuthority::getAuthority).collect(Collectors.toList()))
                    .reduce((t, m) -> {
                        if (t == null)
                            t = new ArrayList<>();
                        t.addAll(m);
                        return t;
                    });

            if (optional.isPresent()) {
                List<TmtAuthority> authorityList = optional.get().stream().distinct().collect(Collectors.toList());
                List<TmtAuthority> parentList = authorityList.stream().filter(x -> x.getParent() == null).collect(Collectors.toList());
                while (parentList.size() > 0) {
                    //现将父菜单加入列表。
                    parentList.forEach(x -> {
                        TmtAuthority authority = new TmtAuthority();
                        authority.setId(x.getId());
                        authority.setAccessPoint(x.getAccessPoint());
                        authority.setSort(x.getSort());
                        authority.setVisible(x.isVisible());
                        authority.setClientTag(x.getClientTag());
                        authority.setOperationType(x.getOperationType());
                        authority.setAuthority(x.getAuthority());
                        authority.setAuthorityType(x.getAuthorityType());
                        authority.setDescription(x.getDescription());
                        authority.setName(x.getName());
                        authority.setClientId(x.getOriginSystem().getClientId());
                        authorities.add(authority);
                        if (x.getParent() != null) {
                            authority.setParentCode(x.getParent().getAuthority());
                            parentMap.put(authority.getId(), x.getParent().getId());
                        }
                    });

                    //后查找下级子菜单。
                    List<TmtAuthority> finalParentList = parentList;
                    parentList = authorityList.stream().filter(x -> x.getParent() != null
                            && finalParentList.contains(x.getParent())).collect(Collectors.toList());
                }
            }

            //重新设置权限的Parent属性。
            parentMap.values().stream().distinct().forEach(x -> {
                Optional<TmtAuthority> optional1 = authorities.stream().filter(y -> Objects.equals(y.getId(), x)).findFirst();
                if (optional1.isPresent()) {

                    List<Long> ids = parentMap.entrySet().stream()
                            .filter(y -> Objects.equals(y.getValue(), x))
                            .map(Map.Entry::getKey)
                            .collect(Collectors.toList());

                    List<TmtAuthority> tmpList = authorities.stream().filter(y -> ids.contains(y.getId()))
                            .collect(Collectors.toList());

                    TmtAuthority parent = optional1.get();
                    tmpList.forEach(y -> y.setParent(parent));
                }
            });

            //给details对象的对应属性赋值。
            details.getTmtAuthorities().addAll(authorities);

        } catch (Exception ex) {
        }
    }

}
